package com.punai.dataanalysis.srlib.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.punai.common.exception.base.BaseException;
import com.punai.common.utils.NumberUtil;
import com.punai.common.utils.PageUtils;
import com.punai.common.utils.poi.ExcelUtil;
import com.punai.config.domain.ConfigMaterialInfo;
import com.punai.config.domain.vo.ConfigMaterialCategoryVo;
import com.punai.config.mapper.ConfigMaterialInfoMapper;
import com.punai.config.service.ConfigMaterialPLCService;
import com.punai.dataanalysis.cblib.bo.MMAInfo;
import com.punai.dataanalysis.srlib.bo.MaterialDetailBo;
import com.punai.dataanalysis.srlib.domain.AnalysisMaterialMachinePriceInfo;
import com.punai.dataanalysis.srlib.domain.AnalysisMaterialMachinePriceRecord;
import com.punai.dataanalysis.srlib.mapper.AnalysisMaterialMachinePriceInfoMapper;
import com.punai.dataanalysis.srlib.mapper.AnalysisMaterialMachinePriceRecordMapper;
import com.punai.dataanalysis.srlib.service.MaterialMachinePriceService;
import com.punai.dataanalysis.srlib.vo.GovernmentPriceVo;
import com.punai.dataanalysis.srlib.vo.MaterialMachinePriceVo;
import com.punai.externalccb.domain.EcoCbfeeitem;
import com.punai.externalccb.mysqlbo.EcoCbfeeitemPriceBo;
import org.apache.commons.collections4.ListValuedMap;
import org.apache.commons.collections4.multimap.ArrayListValuedHashMap;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.stream.Collectors;

@Service
@Transactional
public class MaterialMachinePriceServiceImpl implements MaterialMachinePriceService {

    @Autowired
    private AnalysisMaterialMachinePriceRecordMapper materialMachinePriceRecordMapper;
    @Autowired
    private AnalysisMaterialMachinePriceInfoMapper materialMachinePriceInfoMapper;
    @Autowired
    private ConfigMaterialInfoMapper materialInfoMapper;
    @Autowired
    private ConfigMaterialPLCService configMaterialPLCService;


    @Override
    public List<MaterialMachinePriceVo> getMainTableList(MaterialMachinePriceVo vo) {
        String type = vo.getType();
        String categoryId = vo.getCategoryId();
        List<ConfigMaterialCategoryVo> allCategory = configMaterialPLCService.getAllCategory(categoryId, type);
        List<ConfigMaterialInfo> materials = materialInfoMapper.selectList(
                Wrappers.<ConfigMaterialInfo>lambdaQuery()
                        .eq(ConfigMaterialInfo::getDataStatus,"0")
                        .in(ConfigMaterialInfo::getCategoryId, allCategory.stream()
                                .map(ConfigMaterialCategoryVo::getId)
                                .collect(Collectors.toSet()))
                        .eq(StrUtil.isNotEmpty(vo.getCode()), ConfigMaterialInfo::getCode, vo.getCode())
                        .eq(StrUtil.isNotEmpty(vo.getName()), ConfigMaterialInfo::getName, vo.getName())
                        .eq(StrUtil.isNotEmpty(vo.getSpec()), ConfigMaterialInfo::getSpec, vo.getSpec())
        );
        PageUtils.startPage();
        if(materials.size()==0) return Collections.emptyList();
        return materialMachinePriceInfoMapper.selectListByMaterialIds(
                materials.stream()
                .map(ConfigMaterialInfo::getId)
                .collect(Collectors.toList())
        );
    }

    @Override
    public List<MaterialMachinePriceVo.ExportMianTable> downloadMaterialmachinePL(MaterialMachinePriceVo vo) {
        String type = vo.getType();
        String categoryId = vo.getCategoryId();
        List<ConfigMaterialCategoryVo> allCategory = configMaterialPLCService.getAllCategory(categoryId, type);
        List<ConfigMaterialInfo> materials = materialInfoMapper.selectList(
                Wrappers.<ConfigMaterialInfo>lambdaQuery()
                        .in(ConfigMaterialInfo::getCategoryId, allCategory.stream()
                                .map(ConfigMaterialCategoryVo::getId)
                                .collect(Collectors.toSet()))
                        .eq(ConfigMaterialInfo::getDataStatus, "0")
                        .eq(StrUtil.isNotEmpty(vo.getCode()), ConfigMaterialInfo::getCode, vo.getCode())
                        .eq(StrUtil.isNotEmpty(vo.getName()), ConfigMaterialInfo::getName, vo.getName())
                        .eq(StrUtil.isNotEmpty(vo.getSpec()), ConfigMaterialInfo::getSpec, vo.getSpec())
        );
        if (materials.size() == 0) return Collections.emptyList();
        return materialMachinePriceInfoMapper.selectListByMaterialIds(
                materials.stream()
                        .map(ConfigMaterialInfo::getId)
                        .collect(Collectors.toList())
        ).stream().map(v -> {
            MaterialMachinePriceVo.ExportMianTable re = new MaterialMachinePriceVo.ExportMianTable();
            BeanUtil.copyProperties(v, re);
            return re;
        }).collect(Collectors.toList());
    }

    @Override
    public List<MaterialMachinePriceVo> getTableListByMaterialId(MaterialMachinePriceVo vo) {
        List<MaterialMachinePriceVo> vos = materialMachinePriceInfoMapper.selectListByMaterialId(vo.getMaterialId());
        return vos.stream().filter(v -> {
            boolean b = true;
            if (StrUtil.isNotBlank(vo.getAddress())) b = (StrUtil.equals(vo.getAddress(), v.getAddress()) && b);
            if (StrUtil.isNotBlank(vo.getBrand())) b = (StrUtil.equals(vo.getBrand(), v.getBrand()) && b);
            return b;
        }).collect(Collectors.toList());
    }

    @Override
    public List<String> getTjtMapByMaterialId(MaterialMachinePriceVo vo) {
        return materialMachinePriceInfoMapper.getTjtMapByMaterialId(vo);
    }

    @Override
    public MaterialDetailBo getMMAInfo(MaterialMachinePriceVo vo) {
        MaterialDetailBo bo = new MaterialDetailBo();
        List<MaterialMachinePriceVo> materialMachinePriceVos = materialMachinePriceInfoMapper.selectListByMaterialId(vo.getMaterialId());
        MMAInfo mmaInfo = getMMAInfo(materialMachinePriceVos.stream().filter(v -> {
            boolean b = true;
            if (StrUtil.isNotBlank(vo.getAddress())) b = (StrUtil.equals(vo.getAddress(), v.getAddress()) && b);
            if (StrUtil.isNotBlank(vo.getBrand())) b = (StrUtil.equals(vo.getBrand(), v.getBrand()) && b);
            return b;
        }).collect(Collectors.toList()));
        Set<String> addressList = materialMachinePriceVos.stream().map(MaterialMachinePriceVo::getAddress).collect(Collectors.toSet());
        Set<String> brandList = materialMachinePriceVos.stream().map(MaterialMachinePriceVo::getBrand).collect(Collectors.toSet());

        bo.setMmaInfo(mmaInfo);
        bo.setAddressList(addressList);
        bo.setBrandList(brandList);

        return bo;
    }

    @Override
    public ConfigMaterialInfo selectById(String id) {
        ConfigMaterialInfo configMaterialInfo = materialInfoMapper.selectById(id);
        return configMaterialInfo;
    }

    private MMAInfo getMMAInfo(List<MaterialMachinePriceVo> cbfeeitems) {
        MMAInfo info = new MMAInfo();
        if (CollUtil.isNotEmpty(cbfeeitems)) {
            OptionalDouble average = cbfeeitems.stream().map(MaterialMachinePriceVo::getPriceWithNotTax).filter(targetnotaxprice -> !NumberUtil.isEmpty(targetnotaxprice)).mapToDouble(BigDecimal::doubleValue).average();
            Optional<BigDecimal> max = cbfeeitems.stream().map(MaterialMachinePriceVo::getPriceWithNotTax).filter(targetnotaxprice -> !NumberUtil.isEmpty(targetnotaxprice)).max(BigDecimal::compareTo);
            Optional<BigDecimal> min = cbfeeitems.stream().map(MaterialMachinePriceVo::getPriceWithNotTax).filter(targetnotaxprice -> !NumberUtil.isEmpty(targetnotaxprice)).min(BigDecimal::compareTo);
            info.setAvgPrice(NumberUtil.getValue(average.orElse(0)));
            info.setMaxPrice(max.orElse(BigDecimal.ZERO));
            info.setMinPrice(min.orElse(BigDecimal.ZERO));
        }
        return info;
    }


    @Override
    public Boolean saveRecord(MaterialMachinePriceVo vo) {
        AnalysisMaterialMachinePriceRecord one = null;
        if(StrUtil.isNotEmpty(vo.getId())){
            one = materialMachinePriceRecordMapper.selectById(vo.getId());
        }
        AnalysisMaterialMachinePriceRecord conversion = AnalysisMaterialMachinePriceRecord.conversion(vo, one);
        if(StrUtil.isNotEmpty(conversion.getId()))
            materialMachinePriceRecordMapper.updateById(conversion);
        else
            materialMachinePriceRecordMapper.insert(conversion);
        return true;
    }

    @Override
    public List<AnalysisMaterialMachinePriceRecord> getRecordList(MaterialMachinePriceVo vo) {
        PageUtils.startPage();
        return materialMachinePriceRecordMapper.selectList(Wrappers.<AnalysisMaterialMachinePriceRecord>lambdaQuery()
                .eq(AnalysisMaterialMachinePriceRecord::getDataStatus, "0")
        );
    }

    @Override
    public Boolean deleteRecord(MaterialMachinePriceVo vo) {
        AnalysisMaterialMachinePriceRecord record = materialMachinePriceRecordMapper.selectById(vo.getId());
        record.setDataStatus("1");
        materialMachinePriceRecordMapper.updateById(record);
        materialMachinePriceInfoMapper.delete(Wrappers.<AnalysisMaterialMachinePriceInfo>lambdaQuery()
                .eq(AnalysisMaterialMachinePriceInfo::getRecordId, record.getId()));
        return true;
    }

    @Override
    public List<MaterialMachinePriceVo> getInfoList(MaterialMachinePriceVo vo) {
        PageUtils.startPage();
        return materialMachinePriceInfoMapper.selectAllByRecordId(vo.getRecordId());
    }

    @Override
    public String uploadInfo(String recordId, MultipartFile uploadFile) {
        ExcelUtil<MaterialMachinePriceVo> util = new ExcelUtil<>(MaterialMachinePriceVo.class);
        StringBuilder sb = new StringBuilder();
        try {
            List<MaterialMachinePriceVo> infos = util.importExcel(uploadFile.getInputStream());
            Set<String> codes = new HashSet<>();
            ListValuedMap<String, MaterialMachinePriceVo> valuedHashMap = new ArrayListValuedHashMap<>();
            for (MaterialMachinePriceVo info : infos) {
                String code = info.getCode();
                String brand = StrUtil.emptyToDefault(info.getBrand(),"暂无");
                String address = StrUtil.emptyToDefault(info.getAddress(),"暂无");
                codes.add(code);
                info.setBrand(brand);
                info.setAddress(address);
                valuedHashMap.put(code + "===" + brand + "===" + address,info);
            }
            Map<String, List<ConfigMaterialInfo>> codeMap = materialInfoMapper.selectList(
                    Wrappers.<ConfigMaterialInfo>lambdaQuery()
                            .in(ConfigMaterialInfo::getCode, codes))
                    .stream()
                    .collect(Collectors.groupingBy(ConfigMaterialInfo::getCode));
            Integer order = 0;
            for (String key : valuedHashMap.keySet()) {
                String[] split = key.split("===");
                String code = split[0];
                String brand = split[1];
                String address = split[2];
                MaterialMachinePriceVo infoVo = valuedHashMap.get(key).get(0);
                List<ConfigMaterialInfo> list = codeMap.get(code);
                if (list == null) {
                    sb.append("编码为：").append(code).append("的材料不存在").append("\r\n");
                    continue;
                }
                if(CollUtil.isNotEmpty(list)){
                    ConfigMaterialInfo material = list.get(0);
                    AnalysisMaterialMachinePriceInfo one = ObjectUtil.defaultIfNull(materialMachinePriceInfoMapper.selectOne(Wrappers.<AnalysisMaterialMachinePriceInfo>lambdaQuery()
                            .eq(AnalysisMaterialMachinePriceInfo::getRecordId, recordId)
                            .eq(AnalysisMaterialMachinePriceInfo::getMaterialId, material.getId())
                            .eq(AnalysisMaterialMachinePriceInfo::getBrand, brand)
                            .eq(AnalysisMaterialMachinePriceInfo::getAddress, address)
                    ), new AnalysisMaterialMachinePriceInfo());
                    one.setMaterialId(material.getId());
                    one.setRecordId(recordId);
                    one.setBrand(infoVo.getBrand());
                    one.setAddress(infoVo.getAddress());
                    Map<String, Object> calcPrice = this.calcPrice(material.getCode(),infoVo.getTax(), infoVo.getPriceWithNotTax(), infoVo.getPriceWithTax());
                    if (calcPrice.containsKey("error")) {
                        sb.append(calcPrice.get("error")).append("\r\n");;
                        continue;
                    }
                    one.setPriceWithNotTax(NumberUtil.getValue(calcPrice.get("priceWithNotTax")));
                    one.setPriceWithTax(NumberUtil.getValue(calcPrice.get("priceWithTax")));
                    one.setTax(NumberUtil.getValue(calcPrice.get("tax")));
                    one.setOrderNum(order);
                    one.setRemarks(infoVo.getRemarks());

                    if (StrUtil.isNotBlank(one.getId())) {
                        materialMachinePriceInfoMapper.updateById(one);
                    } else {
                        order++;
                        materialMachinePriceInfoMapper.insert(one);
                    }
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
            throw new BaseException(e.getMessage());
        }
        return sb.toString();
    }
    @Override
    public List<MaterialMachinePriceVo> downloadTemplete(List<String> selIds, String addressId) {
        List<ConfigMaterialInfo> all;
        if(selIds!=null&&selIds.size()>0)
            all = materialInfoMapper.selectList(
                    Wrappers.<ConfigMaterialInfo>lambdaQuery()
                            .in(ConfigMaterialInfo::getId, selIds)
            );
        else
            all = materialInfoMapper.selectList(
                    Wrappers.<ConfigMaterialInfo>lambdaQuery()
                            .eq(ConfigMaterialInfo::getAddressId, addressId)
            );
        return BeanUtil.copyToList(all, MaterialMachinePriceVo.class);
    }
    private Map<String,Object> calcPrice(String code, BigDecimal tax, BigDecimal priceWithNotTax, BigDecimal priceWithTax){
        if((tax == null && priceWithNotTax == null)
                ||(tax == null && priceWithTax == null)
                || (priceWithNotTax == null && priceWithTax == null)){
            return new HashMap<String,Object>(){{
                put("error", "文档中code" + code + "为询价材料含税价，除税价，税率至少有两个不为空!");
            }};
        }
        Map<String, Object> map = new HashMap<>();
        if (priceWithNotTax == null) {
            priceWithNotTax = priceWithTax.divide(new BigDecimal(1).add(tax), 4, RoundingMode.HALF_DOWN);
        }
        if (priceWithTax == null) {
            priceWithTax = priceWithNotTax.multiply(new BigDecimal(1).add(tax)).setScale(4, RoundingMode.HALF_UP);
        }
        if (tax == null) {
            tax = NumberUtil.divide(priceWithTax, 4, BigDecimal.ROUND_DOWN, priceWithNotTax).subtract(BigDecimal.ONE);
        }
        if (NumberUtil.compareTo(priceWithNotTax, priceWithTax) == 1) map.put("error", "文档中code"+ code+"为询价材料存在不含税大于含税!");
        map.put("tax", tax);
        map.put("priceWithNotTax", priceWithNotTax);
        map.put("priceWithTax", priceWithTax);
        return map;
    }
}
